﻿#include "sqldatabase.h"
#include <QThreadStorage>
#include <QString>
#include <QSqlError>
#include <QSqlQuery>
#include <QDebug>

BEGIN_NAMESPACE_CIQTEK

const QString SqlDatabase::g_defaultDBName = "ciqtek.db";
std::atomic_int SqlDatabase::g_atomicConnects = 0;
std::atomic_bool SqlDatabase::g_atomicFlag = false;
QReadWriteLock SqlDatabase::g_lock(QReadWriteLock::Recursive);
thread_local SqlDatabase::ConnectedInfo g_connectInfo;  //基于线程作用域的数值

SqlDatabase::SqlDatabase(QObject *parent)
    : QObject{ parent }
    , m_strDBName("")
{
}

SqlDatabase::~SqlDatabase()
{
    close(m_strDBName);
}

bool SqlDatabase::open(QString strDBName)
{
    // 连接状态检查
    if (strDBName.isEmpty())
    {
        strDBName = g_defaultDBName;
    }

    if (!m_strDBName.isEmpty())
    {
        // 如果打开的连接和上一个不一致，先关闭上一个连接
        if (strDBName != m_strDBName)
        {
            close(m_strDBName);
        }
        else {
            return true;
        }
    }

    // 检查数据是否记录成缓存
    if (!g_connectInfo.isInit)
    {
        // 初始化 缓存
        g_connectInfo.isInit = true;
        g_connectInfo.threadFlag = g_atomicConnects++;
    }

    // 获取缓存
    ConnectedInfo connectInfo = g_connectInfo;

    // 获取当前线程标志
    int threadFlag = connectInfo.threadFlag;

    // 获取连接名称
    QString connectName = getConnectName(strDBName, threadFlag);

    // 判断是否进行过连接
    auto end = connectInfo.dbConnectCount.end();
    bool bFlag = (connectInfo.dbConnectCount.find(connectName) == end);
    if (!bFlag)
    {
        bFlag = (connectInfo.dbConnectCount[connectName] == 0);
    }

    // 没有进行过连接，添加连接
    if (bFlag)
    {
        int connectCount = 0;
        QSqlDatabase database = QSqlDatabase::addDatabase("QSQLITE", connectName);
        database.setDatabaseName(strDBName);
        bool isOpen = database.open();
        if (!isOpen)
        {
            m_strError = database.lastError().text();
            return false;
        }

        m_strDBName = strDBName;
        connectInfo.dbConnectCount[connectName] = ++connectCount;
        g_connectInfo = connectInfo;
        qDebug() << "Open database";
        return true;
    }
    // 进行过连接,已经打开
    else
    {
        int connectCount = connectInfo.dbConnectCount[connectName];
        m_strDBName = strDBName;
        QSqlDatabase database = QSqlDatabase::database(connectName);
        connectInfo.dbConnectCount[connectName] = ++connectCount;
        g_connectInfo = connectInfo;
        return true;
    }
}

bool SqlDatabase::close(QString strDBName)
{
    // 没有打开过数据库，不需要关闭
    if (m_strDBName.isEmpty())
    {
        return true;
    }

    if (strDBName.isEmpty())
    {
        strDBName = m_strDBName;
    }

    // 获取缓存
    //没有数据，代表没有进行初始化，不需要关闭
    if (!g_connectInfo.isInit)
    {
        return true;
    }

    // 判断是否为当前连接,不是则不需要理会
    if (m_strDBName != strDBName)
    {
        return true;
    }

    ConnectedInfo connectInfo = g_connectInfo;
    // 获取当前线程标志
    int threadFlag = connectInfo.threadFlag;

    // 获取连接名称
    QString connectName = getConnectName(strDBName, threadFlag);
    int connectCount = connectInfo.dbConnectCount.value(connectName);
    // 释放数据库连接
    if (connectCount == 1)
    {
        qDebug() << "Close database";
        QSqlDatabase::database(connectName).close();
        QSqlDatabase::removeDatabase(connectName);
    }
    connectInfo.dbConnectCount[connectName] = --connectCount;
    if (connectCount < 0)
    {
        connectInfo.dbConnectCount[connectName] = 0;
    }
    g_connectInfo = connectInfo;
    m_strDBName = "";
    return true;
}

void SqlDatabase::useMultiThread(bool bOpened)
{
    g_atomicFlag = bOpened;
}

bool SqlDatabase::isUseMultiThread()
{
    return g_atomicFlag;
}

QSqlDatabase SqlDatabase::getDatabase(QString strDBName)
{
    if (m_strDBName.isEmpty())
    {
        open(strDBName);
    }

    return QSqlDatabase::database(this->getConnectName(strDBName, g_connectInfo.threadFlag));
}

QString SqlDatabase::getLastError()
{
    return m_strError;
}

QSqlQuery SqlDatabase::exec(const QString &strCmd, bool &bSuccess, bool bIsRead)
{
    if (m_strDBName.isEmpty())
    {
        m_strError = "Database is not open.";
        bSuccess = false;
        return QSqlQuery();
    }

    QSqlQuery query(getDatabase());
    if (isUseMultiThread())
    {
        if (bIsRead)
        {
            g_lock.lockForRead();
        }
        else
        {
            g_lock.lockForWrite();
        }
        bSuccess = query.exec(strCmd);
        g_lock.unlock();
    }
    else
    {
        bSuccess = query.exec(strCmd);
    }

    m_strError = bSuccess ? "" : query.lastError().text();
    return query;
}

QSqlQuery SqlDatabase::insertQuery(const QString &strTableName, const QString &strFields, const QString &strValues, bool &bSuccess)
{
    if (m_strDBName.isEmpty())
    {
        m_strError = "Database is not open.";
        bSuccess = false;
        return QSqlQuery();
    }

    QString cmd = QString("insert into %1(%2) values(%3);")
        .arg(strTableName).arg(strFields).arg(strValues);

    return writeQuery(cmd, bSuccess);
}

QSqlQuery SqlDatabase::insertQuery(const QString &strTableName, const QString &strValues, bool &bSuccess)
{
    if (m_strDBName.isEmpty())
    {
        m_strError = "Database is not open.";
        bSuccess = false;
        return QSqlQuery();
    }

    QString cmd = QString("insert into %1 values(%3);")
        .arg(strTableName).arg(strValues);

    return writeQuery(cmd, bSuccess);
}

QSqlQuery SqlDatabase::insertOrReplaceQuery(const QString &strTableName, const QString &strFields, const QString &strValues, bool &bSuccess)
{
    if (m_strDBName.isEmpty())
    {
        m_strError = "Database is not open.";
        bSuccess = false;
        return QSqlQuery();
    }

    QString cmd = QString("insert or replace into %1(%2) values(%3);")
        .arg(strTableName).arg(strFields).arg(strValues);

    return writeQuery(cmd, bSuccess);
}

QSqlQuery SqlDatabase::insertOrReplaceQuery(const QString &strTableName, const QString &strValues, bool &bSuccess)
{
    if (m_strDBName.isEmpty())
    {
        m_strError = "Database is not open.";
        bSuccess = false;
        return QSqlQuery();
    }

    QString cmd = QString("insert or replace into %1 values(%3);")
        .arg(strTableName).arg(strValues);

    return writeQuery(cmd, bSuccess);
}

QSqlQuery SqlDatabase::deleteQuery(const QString &strTableName, const QString &strWheCondition, bool &bSuccess)
{
    if (m_strDBName.isEmpty())
    {
        m_strError = "Database is not open.";
        bSuccess = false;
        return QSqlQuery();
    }

    QString cmd = QString("delete from %1 where %2;").arg(strTableName).arg(strWheCondition);
    return writeQuery(cmd, bSuccess);
}

QSqlQuery SqlDatabase::deleteQuery(const QString &strTableName, bool &bSuccess)
{
    if (m_strDBName.isEmpty())
    {
        m_strError = "Database is not open.";
        bSuccess = false;
        return QSqlQuery();
    }

    QString cmd = QString("delete from %1;").arg(strTableName);
    return writeQuery(cmd, bSuccess);
}

QSqlQuery SqlDatabase::updateQuery(const QString &strTableName, const QString &strSetFields, bool &bSuccess)
{
    if (m_strDBName.isEmpty())
    {
        m_strError = "Database is not open.";
        bSuccess = false;
        return QSqlQuery();
    }

    QString cmd = QString("update %1 set %2;")
        .arg(strTableName).arg(strSetFields);
    return writeQuery(cmd, bSuccess);
}

QSqlQuery SqlDatabase::updateQuery(const QString &strTableName, const QString &strSetFields, const QString &strWheCondition, bool &bSuccess)
{
    if (m_strDBName.isEmpty())
    {
        m_strError = "Database is not open.";
        bSuccess = false;
        return QSqlQuery();
    }

    QString cmd = QString("update %1 set %2 where %3;")
        .arg(strTableName).arg(strSetFields).arg(strWheCondition);

    return writeQuery(cmd, bSuccess);
}

QSqlQuery SqlDatabase::selectQuery(const QString &strTableName, bool &bSuccess, const QString &strFileds, const QString &strWheCondition,
    const QString &strGroupCondition, const QString &strOrderCondition, int nLimNum, int nOffNum)
{
    if (m_strDBName.isEmpty())
    {
        m_strError = "Database is not open.";
        bSuccess = false;
        return QSqlQuery();
    }

    QString cmd = QString("select %1 from %2 ")
        .arg(strFileds).arg(strTableName);

    if (!strWheCondition.isEmpty())
    {
        QString strWhere = QString(" where %1 ").arg(strWheCondition);
        cmd.append(strWhere);
    }

    if (!strGroupCondition.isEmpty())
    {
        QString strGroupby = QString(" group by %1 ").arg(strGroupCondition);
        cmd.append(strGroupby);
    }

    if (!strOrderCondition.isEmpty())
    {
        QString strOrderby = QString(" order by %1 ").arg(strOrderCondition);
        cmd.append(strOrderby);
    }

    if (nLimNum != -1 && nOffNum != -1)
    {
        QString strLimit = QString(" limit %1 offset %2 ").arg(nLimNum).arg(nOffNum);
        cmd.append(strLimit);
    }

    return readQuery(cmd, bSuccess);
}

QSqlQuery SqlDatabase::checkColumnQuery(const QString &strTableName, const QString &strColumnName, bool &bSuccess)
{
    if (m_strDBName.isEmpty())
    {
        m_strError = "Database is not open.";
        bSuccess = false;
        return QSqlQuery();
    }
    
    QString cmd = QString("SELECT count(*) FROM sqlite_master WHERE name = '%1' and sql like '%%2%';")
        .arg(strTableName).arg(strColumnName);
    return readQuery(cmd, bSuccess);
}

QSqlQuery SqlDatabase::setColumnQuery(const QString &strTableName, const QString &strColumnName, const QString &strProperty, bool &bSuccess)
{
    if (m_strDBName.isEmpty())
    {
        m_strError = "Database is not open.";
        bSuccess = false;
        return QSqlQuery();
    }

    QString cmd = QString("ALTER TABLE %1 ADD COLUMN '%2' %3;")
        .arg(strTableName).arg(strColumnName).arg(strProperty);
    return writeQuery(cmd, bSuccess);
}

void SqlDatabase::lockForRead()
{
    if (g_atomicFlag)
    {
        g_lock.lockForRead();
    }
}

void SqlDatabase::lockForWrite()
{
    if (g_atomicFlag)
    {
        g_lock.lockForWrite();
    }
}

void SqlDatabase::unlock()
{
    if (g_atomicFlag)
    {
        g_lock.unlock();
    }
}

QString SqlDatabase::getConnectName(const QString &strName, const int &nThreadFlag)
{
    QString tempName = strName.split("/").last();
    return QString("%1_%2").arg(tempName).arg(nThreadFlag);
}

QSqlQuery SqlDatabase::readQuery(const QString &cmd, bool &bSuccess)
{
    QSqlQuery query(getDatabase(this->m_strDBName));
    if (isUseMultiThread())
    {
        g_lock.lockForRead();
        bSuccess = query.exec(cmd);
        g_lock.unlock();
    }
    else
    {
        bSuccess = query.exec(cmd);
    }

    m_strError = bSuccess ? "" : query.lastError().text();
    return query;
}

QSqlQuery SqlDatabase::writeQuery(const QString &cmd, bool &bSuccess)
{
    QSqlQuery query(getDatabase(this->m_strDBName));
    if (isUseMultiThread())
    {
        g_lock.lockForWrite();
        bSuccess = query.exec(cmd);
        g_lock.unlock();
    }
    else
    {
        bSuccess = query.exec(cmd);
    }

    m_strError = bSuccess ? "" : query.lastError().text();
    return query;
}

END_NAMESPACE_CIQTEK